-module(cus_user).

-include("cus.hrl").
-include("cus_pb.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").


-behaviour(gen_server).

-export([
    start/0,
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3]).


-export([
    user_info/1,
    login/3,
    logout/1,
    get_conversation_message/3,
    get_conversation_message/4,
    del_conversation/3,
    add_conversation/3,
    update_conversation_read_seq/3,
    add_black/3,
    del_black/3,
    add_follow/2,
    del_follow/2,
    add_stick/2,
    del_stick/2,
    add_muteNotofication/2,
    del_muteNotofication/2,
    add_global_muteNotofication/1,
    del_global_muteNotofication/1,
    add_pushContent/1,
    del_pushContent/1,
    add_user_emotion/5,
    del_user_emotion/2,
    get_user_emotions/1,
    upload_lately_emotions/2,
    revoke_message/2,
    update_device_token/2
        ]).



user_info(Uid) ->
    call(Uid, {user_info, Uid}).


login(Uid, Platform, PushKey) ->
    call(Uid, {login, Uid, Platform, PushKey}).


logout(Uid) ->
    call(Uid, {logout, Uid}).


get_conversation_message(Uid, Cid, Size) ->
    call(Uid, {get_conversation_message, Uid, Cid, Size}).


get_conversation_message(Uid, Cid, Seq, Size) ->
    call(Uid, {get_conversation_message, Uid, Cid, Seq, Size}).


del_conversation(Uid, Cid, Seq) ->
    cast(Uid, {del_conversation, Uid, Cid, Seq}).


add_conversation(Uid, Cid, Seq) ->
    cast(Uid, {add_conversation, Uid, Cid, Seq}).


update_conversation_read_seq(Uid, Cid, Seq) ->
    cast(Uid, {update_conversation_read_seq, Uid, Cid, Seq}).


add_black(Uid, QuanId, BlackUid) ->
    call(Uid, {add_black, Uid, QuanId, BlackUid}).


del_black(Uid, QuanId, BlackUid) ->
    call(Uid, {del_black, Uid, QuanId, BlackUid}).


add_follow(Uid, Cid) ->
    call(Uid, {add_follow, Uid, Cid}).


del_follow(Uid, Cid) ->
    call(Uid, {del_follow, Uid, Cid}).


add_stick(Uid, Cid) ->
    call(Uid, {add_stick, Uid, Cid}).


del_stick(Uid, Cid) ->
    call(Uid, {del_stick, Uid, Cid}).


add_muteNotofication(Uid, Cid) ->
    call(Uid, {add_muteNotofication, Uid, Cid}).


del_muteNotofication(Uid, Cid) ->
    call(Uid, {del_muteNotofication, Uid, Cid}).


add_global_muteNotofication(Uid) ->
    call(Uid, {add_global_muteNotofication, Uid}).


del_global_muteNotofication(Uid) ->
    call(Uid, {del_global_muteNotofication, Uid}).


add_pushContent(Uid) ->
    call(Uid, {add_pushContent, Uid}).


del_pushContent(Uid) ->
    call(Uid, {del_pushContent, Uid}).


add_user_emotion(Uid, Url, JPGUrl, Width, Height) ->
    call(Uid, {add_user_emotion, Uid, Url, JPGUrl, Width, Height}).


del_user_emotion(Uid, UrlList) ->
    call(Uid, {del_user_emotion, Uid, UrlList}).


get_user_emotions(Uid) ->
    call(Uid, {get_user_emotions, Uid}).


upload_lately_emotions(Uid, Data) ->
    call(Uid, {upload_lately_emotions, Uid, Data}).


revoke_message(Uid, Mid) ->
    call(Uid, {revert_message, Uid, Mid}).


update_device_token(Uid, DeviceToken) ->
    call(Uid, {update_device_token, Uid, DeviceToken}).


start() ->
    gen_server:start(?MODULE, [], []).


init([]) ->
    process_flag(trap_exit, true),
    {ok, {}}.


handle_call({user_info, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    {reply, {ok, Info}, State};

handle_call({login, Uid, Platform, PushKey}, _From, State) ->
    Info = get_user_info(Uid),
    Now = cus_util:utc(),
    Info1 = Info#cus_user{platform = Platform, pushKey = PushKey, lastTime = Now},
    UserConversationList = Info1#cus_user.conversations,
    ConversationMessageList = fetch_offline_msg(UserConversationList, []),
    Return = [Info1, ConversationMessageList],
    cus_redis:set_user_info(Info1),
    {reply, {ok, Return}, State};

handle_call({logout, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = Info#cus_user{platform = "", pushKey = ""},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({get_conversation_message, Uid, Cid, Size}, _From, State) ->
    Info = get_user_info(Uid),
    UserConversationList = Info#cus_user.conversations,
    SeqList = get_conversation_seqs(Cid, UserConversationList, Size),
    Return = get_messages(Cid, SeqList, []),
    {reply, {ok, Return}, State};

handle_call({get_conversation_message, Uid, Cid, Seq, Size}, _From, State) ->
    Info = get_user_info(Uid),
    UserConversationList = Info#cus_user.conversations,
    SeqList = get_conversation_seqs(Cid, UserConversationList, Seq, Size),
    Return = get_messages(Cid, SeqList, []),
    {reply, {ok, Return}, State};

handle_call({add_black, Uid, QuanId, BlackUid}, _From, State) ->
    Info = get_user_info(Uid),
    Blacks = Info#cus_user.blacks,
    Source = {QuanId, BlackUid},
    NewBlacks = case ?member(Source, Blacks) of
                    true ->
                        Blacks;
                    _ ->
                        [Source | Blacks]
                end,
    Info1 = Info#cus_user{blacks = NewBlacks},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({del_black, Uid, QuanId, BlackUid}, _From, State) ->
    Info = get_user_info(Uid),
    Blacks = Info#cus_user.blacks,
    Source = {QuanId, BlackUid},
    NewBlacks = case ?member(Source, Blacks) of
                    true ->
                        lists:delete(Source, Blacks);
                    _ ->
                        Blacks
                end,
    Info1 = Info#cus_user{blacks = NewBlacks},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({add_follow, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    Follows = Info#cus_user.follows,
    NewFollows = case ?member(Cid, Follows) of
                     true ->
                         Follows;
                     _ ->
                         [Cid | Follows]
                 end,
    Info1 = Info#cus_user{follows = NewFollows},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({del_follow, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    Follows = Info#cus_user.follows,
    NewFollows = case ?member(Cid, Follows) of
                     true ->
                         lists:delete(Cid, Follows);
                     _ ->
                         Follows
                 end,
    Info1 = Info#cus_user{follows = NewFollows},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({add_stick, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    Sticks = Info#cus_user.sticks,
    NewSticks = case ?member(Cid, Sticks) of
                    true ->
                        Sticks;
                    _ ->
                        [Cid | Sticks]
                end,
    Info1 = Info#cus_user{sticks = NewSticks},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({del_stick, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    Sticks = Info#cus_user.sticks,
    NewSticks = case ?member(Cid, Sticks) of
                    true ->
                        lists:delete(Cid, Sticks);
                    _ ->
                        Sticks
                end,
    Info1 = Info#cus_user{sticks = NewSticks},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({add_muteNotofication, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    MuteNotofications = Info#cus_user.muteNotofications,
    NewMuteNotofications = case ?member(Cid, MuteNotofications) of
                               true ->
                                   MuteNotofications;
                               _ ->
                                   [Cid | MuteNotofications]
                           end,
    Info1 = Info#cus_user{muteNotofications = NewMuteNotofications},
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({del_muteNotofication, Uid, Cid}, _From, State) ->
    Info = get_user_info(Uid),
    MuteNotofications = Info#cus_user.muteNotofications,
    NewMuteNotofications = case ?member(Cid, MuteNotofications) of
                               true ->
                                   lists:delete(Cid, MuteNotofications);
                               _ ->
                                   MuteNotofications
                           end,
    Info1 = Info#cus_user{muteNotofications = NewMuteNotofications},
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({add_global_muteNotofication, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = Info#cus_user{muteNotofication = 1},  %%开启
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({del_global_muteNotofication, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = Info#cus_user{muteNotofication = 0},  %%关闭
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({add_pushContent, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = Info#cus_user{pushContent = 1},  %%拦截
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({del_pushContent, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = Info#cus_user{pushContent = 0},  %%不拦截
    cus_redis:set_user_info(Info1),
    {reply, {ok, Info1}, State};

handle_call({add_user_emotion, Uid, Url, JPGUrl, Width, Height}, _From, State) ->
    Info = get_user_info(Uid),
    Emotions = Info#cus_user.emotions,
    Size = length(Emotions),
    if
        Size >= 200 ->
            {reply, {ok, "to many emotions"}, State};
        true ->
            Source = {Url, JPGUrl, Width, Height},
            NewEmotions = case ?member(Source, Emotions) of
                              true ->
                                  Emotions;
                              _ ->
                                  [Source | Emotions]
                          end,
            Info1 = Info#cus_user{emotions = NewEmotions},
            cus_redis:set_user_info(Info1),
            {reply, {ok, "success"}, State}
    end;

handle_call({del_user_emotion, Uid, UrlList}, _From, State) ->
    Info = get_user_info(Uid),
    Emotions = Info#cus_user.emotions,
    LatelyEmotions = Info#cus_user.latelyEmotions,
    {NewEmotions, NewLatelyEmotions} = lists:foldl(fun(Url, {Acc1, Acc2}) ->
                                                           case lists:keysearch(Url, 1, Emotions) of
                                                               {value, V} ->
                                                                   case lists:keysearch(Url, 1, LatelyEmotions) of   %%查看常用表情中是否存在此表情
                                                                       {value, V} ->
                                                                           {lists:delete(V, Acc1), lists:delete(V, Acc2)};
                                                                       _ ->
                                                                           {lists:delete(V, Acc1), Acc2}
                                                                   end;
                                                               _ ->
                                                                   {Acc1, Acc2}
                                                           end
                                                   end, {Emotions, LatelyEmotions}, UrlList),
    Info1 = Info#cus_user{emotions = NewEmotions,
                          latelyEmotions = NewLatelyEmotions},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({get_user_emotions, Uid}, _From, State) ->
    Info = get_user_info(Uid),
    Emotions = Info#cus_user.emotions,
    {reply, {ok, Emotions}, State};

handle_call({upload_lately_emotions, Uid, Data}, _From, State) ->
    Info = get_user_info(Uid),
    Emotions = Info#cus_user.emotions,
    Fun = fun({Url, _JPGUrl, "0", _Time} = Item, Acc) ->   %%0:自定义表情,1:系统表情
                  case lists:keysearch(Url, 1, Emotions) of
                      {value, _V} ->
                          Acc;
                      _ ->
                          lists:delete(Item, Acc)
                  end;
              (_Item, Acc) ->
                  Acc
          end,
    LatelyEmotions = lists:foldl(Fun, Data, Data),
    Info1 = Info#cus_user{latelyEmotions = LatelyEmotions},
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({update_device_token, Uid, DeviceToken}, _From, State) ->
    Info = get_user_info(Uid),
    Info1 = case Info#cus_user.platform of
                "ios" ->
                    Info#cus_user{pushKey = DeviceToken};
                _ ->
                    Info
            end,
    cus_redis:set_user_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({revoke_message, Uid, Mid}, _From, State) ->
    case cus_redis:get_message(Mid) of
        undefined ->
            {reply, {ok, "0001"}, State};   %% message not exists
        Message ->
            FromUser = Message#msg.fromuser,
            case Uid of
                FromUser ->
                    MsgTime = ?l2i(Message#msg.msgtime) div 1000,
                    Now = cus_util:utc(),
                    if
                        Now - MsgTime < 120 ->
                            Message1 = Message#msg{msgstatus = "1"},
                            cus_redis:set_message(Message1),
                            {reply, {ok, "success"}, State};
                        true ->
                            {reply, {ok, "0003"}, State}    %% revert time not inner 2 min
                    end;
                _ ->
                    {reply, {ok, "0002"}, State}   %% user not match
            end
    end;

handle_call(_Request, _From, State) ->
    {reply, ok, State}.


handle_cast({del_conversation, Uid, Cid, Seq}, State) ->
    Info = get_user_info(Uid),
    UserConversationList = Info#cus_user.conversations,
    case lists:keysearch(Cid, 1, UserConversationList) of
        false ->   %%不存在
            skip;
        {value, {Cid, InitSeq, ReadSeq, _Status}} ->   %%加入时的序号,已读到的序号,会话的当前序号
            Info1 = Info#cus_user{conversations = lists:keyreplace(Cid, 1, UserConversationList, {Cid, InitSeq, ReadSeq, Seq})},
            cus_redis:set_user_info(Info1)
    end,
    {noreply, State};

handle_cast({add_conversation, Uid, Cid, Seq}, State) ->
    Info = get_user_info(Uid),
    UserConversationList = Info#cus_user.conversations,
    Info1 = case lists:keysearch(Cid, 1, UserConversationList) of
                false ->   %%不存在
                    Info#cus_user{conversations = [{Cid, Seq, Seq, infinity} | UserConversationList]};
                {value, {Cid, _InitSeq, _ReadSeq, _Status}} ->   %%加入时的序号,已读到的序号,会话的当前序号
                    Info#cus_user{conversations = lists:keyreplace(Cid, 1, UserConversationList, {Cid, Seq, Seq, infinity})}
            end,
    cus_redis:set_user_info(Info1),
    {noreply, State};

handle_cast({update_conversation_read_seq, Uid, Cid, Seq}, State) ->
    Info = get_user_info(Uid),
    UserConversationList = Info#cus_user.conversations,
    case lists:keysearch(Cid, 1, UserConversationList) of
        false ->
            skip;
        {value, {Cid, InitSeq, ReadSeq, Status}} ->
            if
                Seq > ReadSeq ->
                    Info1 = Info#cus_user{conversations = lists:keyreplace(Cid, 1, UserConversationList, {Cid, InitSeq, Seq, Status})},
                    cus_redis:set_user_info(Info1);
                true ->
                    skip
            end
    end,
    {noreply, State};

handle_cast(_Msg, State) ->
    {noreply, State}.

    
handle_info(_Info, State) ->
    {noreply, State}.


terminate(_Reason, _State) ->
    ok.
    

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


fetch_offline_msg([], Acc) ->
    Acc;
fetch_offline_msg([{ConversationID, _InitSeq, ReadSeq, Status} | T], Acc) ->
    AllSeq = get_all_seq(ConversationID, Status),
    Seqs = get_offline_seqs(AllSeq, ReadSeq),
    case Seqs of
        [] ->   %%不存在离线消息
            fetch_offline_msg(T, Acc);
        _ ->    %%存在离线消息
            Messages = get_messages(ConversationID, Seqs, []),
            Total = AllSeq - ReadSeq,
            fetch_offline_msg(T, [{Total, Messages} | Acc])
    end.
   

get_all_seq(Cid, infinity) ->
    Info = cus_conversation:conversation_info(Cid),
    Info#cus_conversation.seq;
get_all_seq(_Cid, Status) ->
    Status.


get_offline_seqs(AllSeq, ReadSeq) ->
    Diff = AllSeq - ReadSeq,
    if
        Diff > 20 ->    %%最多返回最后20条离线消息
            lists:seq(AllSeq - 19, AllSeq);
        Diff > 0 andalso Diff =< 20 ->
            lists:seq(ReadSeq + 1, AllSeq);
        true ->
            []
    end.
    

get_messages(_ConversationID, [], Acc) ->
    Acc;
get_messages(ConversationID, [Seq | T], Acc) ->
    Mid = lists:concat([ConversationID, "_", Seq]),
    case cus_redis:get_message(Mid) of
        undefined ->
            get_messages(ConversationID, T, Acc);
        Message ->
            get_messages(ConversationID, T, [Message | Acc])
    end.


get_user_info(Uid) ->
    case cus_redis:get_user_info(Uid) of
        undefined ->
            Info = #cus_user{uid = Uid},
            cus_redis:set_user_info(Info),
            Info;
        Info ->
            Info
    end.


get_conversation_seqs(ConversationID, ConversationList, Size) ->
    case lists:keysearch(ConversationID, 1, ConversationList) of
        false ->
            [];
        {value, {ConversationID, InitSeq, _ReadSeq, Status}} ->
            AllSeq = get_all_seq(ConversationID, Status),
            if
                AllSeq - InitSeq >= Size ->
                    lists:seq(AllSeq - Size + 1, AllSeq);
                AllSeq - InitSeq < Size ->
                    lists:seq(InitSeq + 1, AllSeq)
            end
    end.


get_conversation_seqs(ConversationID, ConversationList, Seq, Size) ->
    case lists:keysearch(ConversationID, 1, ConversationList) of
        false ->
            [];
        {value, {ConversationID, InitSeq, _ReadSeq, _Status}} ->
            if
                Seq - InitSeq >= Size ->
                    lists:seq(Seq - Size, Seq - 1);
                Seq - InitSeq < Size ->
                    if
                        Seq > InitSeq ->
                            lists:seq(InitSeq + 1, Seq - 1);
                        true ->
                            []
                    end
            end
    end.


call(Uid, Msg) ->
    cus_proc_manage:user_call(Uid, Msg).



cast(Uid, Msg) ->
    cus_proc_manage:user_cast(Uid, Msg).
